/**
 * 
 */
package com.ayuan.blog.common.httpclient;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.log4j.Log4j2;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.core.MediaType;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Log4j2
public class HTTPClientUtils {

	private static final JsonNodeFactory factory = new JsonNodeFactory(false);

	/**
	 * Send SSL Request
	 * 
	 * @return
	 */
	public static ObjectNode sendHTTPRequest(String urlString, HTTPMethod.Credential credential, Object dataBody, String method) {
      
		HttpClient httpClient = getClient(true);
        URI uri = HTTPClientUtils.getURI(urlString);
		ObjectNode resObjectNode = factory.objectNode();

		try {
			StringEntity entity = new StringEntity(String.valueOf(dataBody), "UTF-8");
			if(log.isDebugEnabled()){
				log.debug("[sendHTTPRequest]:"+String.valueOf(dataBody));
			}
			HttpResponse response = null;

			if (method.equals(HTTPMethod.METHOD_POST)) {
				HttpPost httpPost = new HttpPost(uri);

				if (credential != null) {
					Token.applyAuthentication(httpPost, credential);
				}
				httpPost.setEntity(entity);

				response = httpClient.execute(httpPost);
			} else if (method.equals(HTTPMethod.METHOD_PUT)) {
				HttpPut httpPut = new HttpPut(uri);
				if (credential != null) {
					Token.applyAuthentication(httpPut, credential);
				}
				httpPut.setEntity(entity);

				response = httpClient.execute(httpPut);
			} else if (method.equals(HTTPMethod.METHOD_GET)) {

				HttpGet httpGet = new HttpGet(uri);
				if (credential != null) {
					Token.applyAuthentication(httpGet, credential);
				}

				response = httpClient.execute(httpGet);

			} else if (method.equals(HTTPMethod.METHOD_DELETE)) {
				HttpDelete httpDelete = new HttpDelete(uri);

				if (credential != null) {
					Token.applyAuthentication(httpDelete, credential);
				}

				response = httpClient.execute(httpDelete);
			}

			HttpEntity responseEntity = response.getEntity();
			if (null != responseEntity) {
				String responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new UtilityRuntimeException(e.getMessage(), e);
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return resObjectNode;
	}
	
	public static void inputstreamtofile(InputStream ins,File file){
		OutputStream os;
		try {
			os = new FileOutputStream(file);
			int bytesRead = 0;
			byte[] buffer = new byte[8192];
			try {
				while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
					os.write(buffer, 0, bytesRead);
					}
				os.close();
				ins.close();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
				throw new UtilityRuntimeException(e.getMessage(), e);
			}
		} catch (FileNotFoundException e) {
			log.error(e.getMessage(), e);
			throw new UtilityRuntimeException(e.getMessage(), e);
		}
		
		}

	/**
	 * Send SSL Request
	 * 
	 * @return
	 */
	public static ObjectNode sendHTTPRequest(String urlString,  Object dataBody, String method) {
      
		HttpClient httpClient = getClient(true);
        URI uri = HTTPClientUtils.getURI(urlString);
		ObjectNode resObjectNode = factory.objectNode();

		try {
			StringEntity entity = new StringEntity(String.valueOf(dataBody), "UTF-8");
			HttpResponse response = null;

			if (method.equals(HTTPMethod.METHOD_POST)) {
				HttpPost httpPost = new HttpPost(uri);
				httpPost.setEntity(entity);

				response = httpClient.execute(httpPost);
			} else if (method.equals(HTTPMethod.METHOD_PUT)) {
				HttpPut httpPut = new HttpPut(uri);
				httpPut.setEntity(entity);

				response = httpClient.execute(httpPut);
			} else if (method.equals(HTTPMethod.METHOD_GET)) {

				HttpGet httpGet = new HttpGet(uri);
				response = httpClient.execute(httpGet);

			} else if (method.equals(HTTPMethod.METHOD_DELETE)) {
				HttpDelete httpDelete = new HttpDelete(uri);
				response = httpClient.execute(httpDelete);
			}

			HttpEntity responseEntity = response.getEntity();
			if (null != responseEntity) {
				String responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			String errMsg = String
					.format("Exception when sendHTTPRequest with url %s, dataBody %s and method %s: %s",
							urlString, String.valueOf(dataBody), method,
							e.getMessage());
			log.error(errMsg, e);
			throw new UtilityRuntimeException(errMsg, e);
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return resObjectNode;
	}

	/**
	 * Send SSL Request
	 * 
	 * @return
	 */
	public static ObjectNode sendHTTPRequest(String urlString,  Object dataBody, String method,Map<String, String> headers) {
      
		HttpClient httpClient = getClient(true);
        URI uri = HTTPClientUtils.getURI(urlString);
		ObjectNode resObjectNode = factory.objectNode();

		try {
			StringEntity entity = new StringEntity(String.valueOf(dataBody), "UTF-8");
			HttpResponse response = null;

			if (method.equals(HTTPMethod.METHOD_POST)) {
				HttpPost httpPost = new HttpPost(uri);
				httpPost.setEntity(entity);
				if(headers != null && headers.size() > 0){
					Iterator<String> iterator = headers.keySet().iterator();
					while (iterator.hasNext()) {
						String key = (String) iterator.next();
						httpPost.addHeader(key, headers.get(key));
					}
				}

				response = httpClient.execute(httpPost);
			} else if (method.equals(HTTPMethod.METHOD_PUT)) {
				HttpPut httpPut = new HttpPut(uri);
				httpPut.setEntity(entity);
				if(headers != null && headers.size() > 0){
					Iterator<String> iterator = headers.keySet().iterator();
					while (iterator.hasNext()) {
						String key = (String) iterator.next();
						httpPut.addHeader(key, headers.get(key));
					}
				}

				response = httpClient.execute(httpPut);
			} else if (method.equals(HTTPMethod.METHOD_GET)) {

				HttpGet httpGet = new HttpGet(uri);
				if(headers != null && headers.size() > 0){
					Iterator<String> iterator = headers.keySet().iterator();
					while (iterator.hasNext()) {
						String key = (String) iterator.next();
						httpGet.addHeader(key, headers.get(key));
					}
				}
				response = httpClient.execute(httpGet);

			} else if (method.equals(HTTPMethod.METHOD_DELETE)) {
				HttpDelete httpDelete = new HttpDelete(uri);
				if(headers != null && headers.size() > 0){
					Iterator<String> iterator = headers.keySet().iterator();
					while (iterator.hasNext()) {
						String key = (String) iterator.next();
						httpDelete.addHeader(key, headers.get(key));
					}
				}
				response = httpClient.execute(httpDelete);
			}

			HttpEntity responseEntity = response.getEntity();
			if (null != responseEntity) {
				String responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			String errMsg = String
					.format("Exception when sendHTTPRequest with url %s, dataBody %s and method %s: %s",
							urlString, String.valueOf(dataBody), method,
							e.getMessage());
			log.error(errMsg, e);
			throw new UtilityRuntimeException(errMsg, e);
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return resObjectNode;
	}
	
	/**
	 * DownLoadFile whit Jersey
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 * @throws IOException
	 */
	public static File downLoadFile(URL url, HTTPMethod.Credential credential, List<NameValuePair> headers, File localPath) {

		HttpClient httpClient = getClient(true);

		try {

			HttpGet httpGet = new HttpGet(url.toURI());

			if (credential != null) {
				Token.applyAuthentication(httpGet, credential);
			}
			for (NameValuePair header : headers) {
				httpGet.addHeader(header.getName(), header.getValue());
			}

            HttpResponse response = httpClient.execute(httpGet);

			HttpEntity entity = response.getEntity();
			InputStream in = entity.getContent();
			FileOutputStream fos = new FileOutputStream(localPath);

			byte[] buffer = new byte[1024];
			int len1 = 0;
			while ((len1 = in.read(buffer)) != -1) {
				fos.write(buffer, 0, len1);
			}

			fos.close();

		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new UtilityRuntimeException(e.getMessage(), e);
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return localPath;
	}

	/**
	 * UploadFile whit Jersey
	 * 
	 * @return
	 */
	public static ObjectNode uploadFile(URI url, File file, HTTPMethod.Credential credential, List<NameValuePair> headers)
			throws RuntimeException {
		HttpClient httpClient = getClient(true);

		ObjectNode resObjectNode = factory.objectNode();

		try {
			HttpPost httpPost = new HttpPost(url);

			if (credential != null) {
				Token.applyAuthentication(httpPost, credential);
			}
			for (NameValuePair header : headers) {
				httpPost.addHeader(header.getName(), header.getValue());
			}

			MultipartEntity mpEntity = new MultipartEntity();
			ContentBody cbFile = new FileBody(file, MediaType.APPLICATION_OCTET_STREAM);
			mpEntity.addPart("file", cbFile);
			httpPost.setEntity(mpEntity);

            HttpResponse response = httpClient.execute(httpPost);

			HttpEntity entity = response.getEntity();

			if (null != entity) {
				String responseContent = EntityUtils.toString(entity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new UtilityRuntimeException(e.getMessage(), e);
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return resObjectNode;
	}

	/**
	 * Create a httpClient instance
	 * 
	 * @param isSSL
	 * @return HttpClient instance
	 */
	public static HttpClient getClient(boolean isSSL) {

		HttpClient httpClient = new DefaultHttpClient();
		if (isSSL) {
			X509TrustManager xtm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}

				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};

			try {
				SSLContext ctx = SSLContext.getInstance("TLS");

				ctx.init(null, new TrustManager[] { xtm }, null);

				SSLSocketFactory socketFactory = new SSLSocketFactory(ctx);

				httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));

			} catch (Exception e) {
				log.error(e.getMessage(), e);
				throw new UtilityRuntimeException(e.getMessage(), e);
			}
		}

		return httpClient;
	}

	public static URI getURI(String path) {
		URI uri = null;
	

		try {
			URL url = new URL(path);
			if(url.getPort() > 0) {
				uri = new URI(url.getProtocol(), null, url.getHost(), url.getPort(), url.getPath(), url.getQuery(), null);
			} else {
				uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), null);
			}
			
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}

		return uri;
	}

	/**
	 * Check illegal String
	 * 
	 * @param regex
	 * @param str
	 * @return
	 */
	public static boolean match(String regex, String str) {
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);

		return matcher.lookingAt();
	}

	public static URL getURL(String format) {
		// TODO Auto-generated method stub
		URL url = null;
		

		try {
			 url = new URL(format);
			
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} 

		return url;
	}
	
	public static ObjectNode postHttpRequest(String urlString, Object dataBody,
			List<NameValuePair> headers) {
		HttpClient httpClient = getClient(true);
		URI uri = HTTPClientUtils.getURI(urlString);
		ObjectNode resObjectNode = factory.objectNode();
		String responseContent = null;
		try {
			StringEntity entity = new StringEntity(String.valueOf(dataBody),
					"UTF-8");
			HttpResponse response = null;
			HttpPost httpPost = new HttpPost(uri);
			httpPost.setEntity(entity);
			if(headers != null) {
				for(NameValuePair nameValuePair: headers) {
					httpPost.addHeader(nameValuePair.getName(), nameValuePair.getValue());
				}
			}
			response = httpClient.execute(httpPost);
			HttpEntity responseEntity = response.getEntity();

			if (null != responseEntity) {
				responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			String msg = String.format(
					"Exception met when send request to %s with dataBody %s and the response is: %s",
					urlString, String.valueOf(dataBody), responseContent);
			log.error(msg, e);
			throw new UtilityRuntimeException(msg, e);
		}
		return resObjectNode;
	}
	
	
	public static ObjectNode postHttpFormData(String urlString, List<NameValuePair> parameters) {
		HttpClient httpClient = getClient(true);
		URI uri = HTTPClientUtils.getURI(urlString);
		ObjectNode resObjectNode = factory.objectNode();

		try {
			UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(
                    parameters,"UTF-8");
			HttpResponse response = null;
			HttpPost httpPost = new HttpPost(uri);
			httpPost.setEntity(formEntity);
			
			response = httpClient.execute(httpPost);
			
			HttpEntity responseEntity = response.getEntity();
			if (null != responseEntity) {
				String responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(formEntity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return resObjectNode;
	}
	
	public static ObjectNode postHttpJsonData(String url, Object dataBody) {
		HttpClient httpClient = getClient(true);
		URI uri = HTTPClientUtils.getURI(url);
		ObjectNode resObjectNode = factory.objectNode();

		try {
			StringEntity entity = new StringEntity(JSON.toJSONString(dataBody),
					"UTF-8");
			HttpResponse response = null;
			HttpPost httpPost = new HttpPost(uri);
			httpPost.setEntity(entity);
			httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
			entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
			response = httpClient.execute(httpPost);
			
			HttpEntity responseEntity = response.getEntity();
			if (null != responseEntity) {
				String responseContent = EntityUtils.toString(responseEntity, "UTF-8");
				EntityUtils.consume(entity);

				ObjectMapper mapper = new ObjectMapper();
				JsonFactory factory = mapper.getJsonFactory();
				JsonParser jp = factory.createJsonParser(responseContent);

				resObjectNode = mapper.readTree(jp);
				if (null == resObjectNode) {
					resObjectNode = HTTPClientUtils.factory.objectNode();
				}
				resObjectNode.put("statusCode", response.getStatusLine().getStatusCode());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return resObjectNode;
	}
	
	public static ObjectNode postHttpFormData(String urlString, ObjectNode formValues) {
		List<NameValuePair> parameters = new ArrayList<NameValuePair>();
		Iterator<String> iterator = 
				formValues.fieldNames();
		while (iterator.hasNext()) {
			String fieldName = (String) iterator.next();
			JsonNode valueNode = formValues.get(fieldName);
			String fieldValue = null;
			if(valueNode instanceof ObjectNode){
				//TODO
			}else{
				fieldValue = valueNode.asText();
			}
			NameValuePair pare =
					new BasicNameValuePair(fieldName, fieldValue);
			parameters.add(pare);
		}
		return postHttpFormData(urlString, parameters);
	}
	
	public static void main(String[] args) {
		List<NameValuePair> parameters = new ArrayList<NameValuePair>();
		NameValuePair username = new BasicNameValuePair("username","devtest002");
		NameValuePair password = new BasicNameValuePair("password","000000");
		parameters.add(username);
		parameters.add(password);
		ObjectNode response = postHttpFormData("http://112.124.28.82:6005/login",parameters);
		System.out.println(response);
		
	}
}

